/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "plugins/ecmascript/runtime/base/builtins_base.h"
#include "plugins/ecmascript/runtime/ecma_vm.h"
#include "plugins/ecmascript/runtime/global_env.h"
#include "plugins/ecmascript/runtime/internal_call_params.h"
#include "plugins/ecmascript/runtime/js_weak_container.h"
#include "plugins/ecmascript/runtime/object_factory.h"

namespace ark::ecmascript::builtins {
JSTaggedValue weak_ref::Constructor(EcmaRuntimeCallInfo *argv)
{
    ASSERT(argv);
    BUILTINS_API_TRACE(argv->GetThread(), WeakRef, Constructor);
    JSThread *thread = argv->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    // 1.If NewTarget is undefined, throw a TypeError exception
    JSHandle<JSTaggedValue> newTarget = builtins_common::GetNewTarget(argv);
    if (newTarget->IsUndefined()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "new target must not be undefined", JSTaggedValue::Exception());
    }
    // 2. If Type(target) is not Object, throw a TypeError exception.
    JSHandle<JSTaggedValue> target = builtins_common::GetCallArg(argv, 0);
    if (!target->IsECMAObject()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "target must not be undefined", JSTaggedValue::Exception());
    }
    // 3. Let WeakRef be OrdinaryCreateFromConstructor(NewTarget, "%WeakRefPrototype%", «[[WeakRefTarget]]» ).
    JSHandle<JSTaggedValue> constructor = builtins_common::GetConstructor(argv);
    JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    JSHandle<JSWeakRef> weakRef = JSHandle<JSWeakRef>::Cast(obj);

    // 4. Perform AddToKeptObjects(target).
    // 5. Set weakRef.[[WeakRefTarget]] to target.
    weakRef->SetReferent(thread, target.GetTaggedValue());
    // Return weakRef.
    return weakRef.GetTaggedValue();
}

JSTaggedValue weak_ref::proto::Deref(EcmaRuntimeCallInfo *argv)
{
    ASSERT(argv);
    BUILTINS_API_TRACE(argv->GetThread(), WeakRefPrototype, Deref);
    JSThread *thread = argv->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> self = builtins_common::GetThis(argv);
    // 1. Let weakRef be the this value.
    // 2. Perform ? RequireInternalSlot(weakRef, [[WeakRefTarget]]).
    if (!self->IsJSWeakRef()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakRef.", JSTaggedValue::Exception());
    }
    JSHandle<JSWeakRef> weakRef(self);
    // 3. Return WeakRefDeref(weakRef).
    return weakRef->GetReferent();
}
}  // namespace ark::ecmascript::builtins
